热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

轨道|片断_AVFoundation学习笔记:媒体的创建与编辑

篇首语:本文由编程笔记#小编为大家整理,主要介绍了AVFoundation学习笔记:媒体的创建与编辑相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了AVFoundation学习笔记: 媒体的创建与编辑相关的知识,希望对你有一定的参考价值。




  • 组合媒体
  • 时间处理
    • CMTime
    • CMTimeRange
  • 创建组合资源
    • 示例
  • 音频混合
    • 自动调节音量
  • 视频过渡
    • AVVideoComposition
    • AVVideoCompositionInstruction
    • AVVideoCompositionLayerInstruction
    • 部署视频
    • 计算通过和过渡的时间范围
    • 创建组合和层指令
    • 创建和配置AVVideoComposition
    • 应用过渡效果
      • 溶解
      • 推入
      • 擦除
    • 演练
  • 动画层内容
    • AVSynchronizedLayer
    • AVVideoCompositionCoreAnimationTool
    • 导出CoreAnimation
    • 演练




组合媒体

媒体组合就是将需要的相关片断从资源中提取出来,再将他们组合成一个临时排列。AVFoundation明确定义了相关功能,可以让开发者简单地将多个音频和视频资源组合成一个新资源。

AVFoundation有关资源组合的功能源于AVAsset的子类AVComposition。AVComposition中的轨道都是AVAssetTrack的子类AVCompositionTrack。一个组合轨迹本身由一个或多个媒体片断组成,由AVCompositionTrackSegment类定义,代表这个组合中的实际媒体区域,描述如下图所示:

AVComposition和AVCompositionTrack都是不可变对象,提供对资源的只读操作。这些对象提供了一个合适的接口让应用程序的一部分可以进行播放或处理。但当需要创建自己的组合时,就需要使用AVMutableComposition和AVMutableCompositionTrack所提供的可变子类。要创建自定义组合,需指定在将要添加到组合的源媒体的时间范围,还要指定要添加片段的每个轨道的位置。


时间处理

CMTime

通常苹果开发者使用NSTimeInterval表示时间,虽然在大多数情况下,它可以满足要求,但是它的不精确性导致它无法用于更高级的媒体开发中。因此CoreMedia框架中定义了CMTime数据类型作为时间格式。定义如下:

typedef struct
CMTimeValue value;
CMTimeScale timescale;
CMTimeFlags flags;
CMTimeEpoch epoch;
CMTime;

其中,CMTimeValue和CMTimeScale分别是64位和32位有符号整数,CMTime是分数形式,CMTimeFlags是一个掩码位,用于表示时间的指定定状态,我们可以使用CMTimeMake函数来创建一个时间,并使用CMTimeShow将时间值输出到控制台。另外,还有CMTimeAdd和CMTimeSubtract函数可以简单的完成时间的加减。


CMTimeRange

CoreMedia框架还为时间范围提供了一个数据类型,称为CMTimeRange,它在有关资源编辑的API中有重要地位。CMTimeRangem上两个CMTime组成,第一个表示时间范围的起点,第二个值表示时间范围的持续时间。定义如下:

typedef struct
CMTime start;
CMTime duration;
CMTimeRange;

同样的,我们可以使用CMTimeRangeMake函数来创建一个CMTimeRange,使用CMTimeRangeShow输出到控制台,另外一种创建的方法是CMTimeRangeTimeToTime,提供一个时间范围的起点和终点的CMTime值来创建。


创建组合资源

使用loadValuesAsynchronouslyForKeys:completionHandler:加载资源之后,就可以开始创建组合资源。通用代码如下所示:

AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack* videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo perferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableComposition* audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio perferredTrackID:kCMPersistentTrackID_Invalid];

上面的示例创建了一个AVMutableComposition并用它的addMutableTrackWithMediaType:perferredTrackID:方法添加了两条轨道,并为其设置两个ID,这个值通用值kCMPersistentTrackID_Invalid常量,这个常量的意思是我们需要创建一个合适轨道的ID的任务委托给框架,标识符会以1..n排列。示意图如下:

创建两个轨道之后, 接下来就是将独立的媒体片段插入到组合的轨道内,代码如下所示:

说明如下:


  1. 定义一个CMTime变量cursorTime来表示我们所指的插入光标点。轨道的这个时间点就是我们插入媒体片段的位置。
  2. 我们取视频片断前5秒钟的内容,所以创建一个CMTimeRange,令其从kCMTimeZero开始,持续时间5秒。
  3. 使用tracksWithMediaType:方法从第一个AVAsset中提取视频轨道,然后使用insertTimeRange:ofTrack:atTime:error:将视频轨道插入到轨道中。
  4. 使用CMTimeAdd函数来移动光标的插入时间,使得下一段内容可以在另一段内容最后插入。
  5. 同样的,提取资源视频轨道并将其插入到组合资源的视频轨道上
  6. 重置cursorTime,同样的插入对应的音频轨道,此时轨道示视图如下:


示例

如下是我随便截取自《Titanic》的两个片断,本来想上传gif的,但是CSDN限制只能低于2M,怎么搞都搞不出来,所以只能上传两张静态图片:

然后使用上述代码,最终将得到两个片断中的部分内容,并且播放的是第一个视频的声音,如下所示:


音频混合

我们在日常生活中经常有这种场景,开始播放声音的时候,音量从小到大,到结束的时候,声音又从大到小。AVFoundation提供了AVAudioMix来实现这些功能。

AVAudioMix所具有的音频处理方法是由它的输入参数集定义的,它的参数是AVAudioMixInputParameters类型的对象。AVAudioMixInputParameters的实例关联组合中的单独音频轨道,并在添加到音频混合时定义基于轨道的处理方法。AVAudioMix和其他相关的AVAudioMixInputParameters集合都是不可变对象,意味着它个适用于火AVPlayerItem和其相关联的AVAssetExportSession之类的客户端提供相关数据,但不能操作它们的状态,当需要创建一个自定义音频混合时,需要改用它们的可变子类AVMutableAudioMix和AVMutableAudioMixInputParameters。


自动调节音量

在应用音频混合的问题上,最核心的处理就是调整组合音频轨道的音量。当一个组合资源播放或导出时,默认行为是以最大音量或正常音量播放音频轨道。当只有一个单音频轨道时这样的方法才能被接收,但当一个组合资源包含多个音频轨道时就会出现问题,因为每个声音都在争夺设备。这就不可避免会导致一些声音可能无法被听到。AVFoundation使用一种更简单的模型,把音量定义为一个标准化的浮点型数值,范围从0.0(静音)〜1.0(最大音量),不过可以使用AVMutableAudioMixInputParameters实例修改这个值。这个对象允许在一个指定时间点或给定的时间范围自动调节音量。AVMutableAudioMixInputParameters提供了两个方法来实现音量调节:


  • setVolume:atTime: 在指定时间点立即调节音量。音量在音频轨道持续时间内会保持不变,直到有另外一个音量调节出现。
  • setVolumeRampFromStartVolume:toEndVolume:timeRange: 允许在一个给定时间范围内平滑地将音量从一个值调节到另外一个值。当需要在一个时间范围内调整音量时,音量会立即变为指定值的初始音量并在持续时间内逐渐调整到指定的结束值。

实例代码如下:

首先创建一个新的与要操作的轨道关联的AVMutableAudioMixInputParameters实例。轨道的默认音量是1.0,然后设置开始时的音量为0.5, 在轨道中选定2秒,然后使用setVolumeRampFromStartVolume:toEndVolume:timeRange:方法应用一个从0.5~0.8的音量渐变。最后在7秒的时候将音量下调到0.3。之后创建AVMutableAudioMix,并将参数添加到对象中,然后将AVMutableAudioMix设置到AVPlayerItem或AVAssetExportSession的audioMix属性进行播放或导出。如下图所示:


视频过渡

大部分视频编辑应用程序的一个重要功能就是创建动态视频过渡效果。AVFoundation对这一功能的支持提供了很高的可靠性。


AVVideoComposition

视频过渡类API中最核心的类是AVVideoComposition。这个类对两个或多个视频轨道组合在一起的方法给出了一个总体描述。它由一组时间范围和描述组合行为的介绍内容组成。这些信息出现在组合资源内的任意时间点。除了包含描述输入视频层组合的信息之外,还提供了配置视频组合的渲染尺寸、缩放和帧时长等属性。


AVVideoCompositionInstruction

AVVideoComposition是由一组AVVideoCompositionInstruction对象格式定义的指令组成。这个对象所提供的最关键的一段数据是组合对象时间轴内的时间范围信息。这一时间范围是在某一组合形式出现时的时间范围。要执行的组全特质是通过其layerInstructions集合定义的。


AVVideoCompositionLayerInstruction

AVVideoCompositionLayerInstruction用于定义对给定视频轨道应用的模糊、变形和裁剪效果。它提供了一些方法用于在特定的时间点或在一个时间范围内对这些值进行修改。在一段时间内对这些值应用渐变操作可以让开发者创建出动态的过渡效果,如溶解或渐淡。

与所有AVFoundation媒体编辑类一样,视频组合API具有不可变或可变两种形式。不可变对象适用于客户端对象,如AVPlayerItem或AVAssetExportSession,不过当我们创建自己的视频组合应用程序时,应该使用可变子类。


部署视频

要在剪辑间添加过渡,首先需要将两个轨道间的视频段重新部署。大多数情况下,两个轨道就可以满足需求,不过为了满足一些特殊需求而加入更多轨道也是可以的。首先,我们创建两个视频轨道,如下所示:

AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack* trackA = [composition addMutableTrackWithMediaType:AVMediaTypeVideo perferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack* trackB = [composition addMutableTrackWithMediaType:AVMediaTypeVideo perferredTrackID:kCMPersistentTrackID_Invalid];
NSArray* videoTracks = @[trackA, trackB];

要添加过渡效果,需要以交错方式排列视频片断,并且需要根据过渡时间的持续时长来确定片段的重叠情况。如下示意图:

如图,两个交错的片断在左右两侧均有一定时间的交错。如果不这样,就无法实现视频过渡效果。代码如下所示:


计算通过和过渡的时间范围

AVVideoComposition由一组AVVideoCompositionInstruction对象组成。其中最重要的数据是时间范围,它用来表示某种出现的组合方式持续的时长。在开始创建AVVideoCompositionInstruction实例前,首先需要为组合对象计算一系列时间范围。

需要计算两个类型的时间范围,第一个通常被认为是通过(pass-through)时间范围,在这个时间范围内希望一个轨道的所有帧序列都不在与其轨道进行混合的情况下通过某一区域。第二个时间范围类型为过渡(transition)时间范围。它定义了在组合中视频片段重叠的区域,并在时间轴上标记出应用过渡效果的区域。如下图所示:

创建所需的过渡时间范围被认为是整个处理过程中最重要的一步操作。虽然从表面上看,它并不是最难解决的问题,但确非常容易出错。在进行这一步处理时一定要记住两点:


  • 计算时间范围必须没有任何空隙或重叠,必须是紧接上一个片段之后按需排列的最新时间范围。
  • 计算必须考虑组合对象持续时间。如果组合中还包含额外的轨道,就需要使它们遵循目前的视频时间轴,或者根据它们的持续时长扩展最终的时间范围。

如果没有注意到以上两点,组合对象仍可播放,不过视频内容不会被渲染,只会显示一个黑屏。苹果提供了一个工具类,可以帮助开发者诊断这些问题。在Apple Developer Center中能找到一个AVCompositionDebugViewer的项目,可以帮助我们呈现组合。


创建组合和层指令

接下来是创建AVVideoCompositionInstruction和AVVideoCompositionLayerInstruction实例,代码如下:


  1. 首先遍历之前计算的所有通过时间的范围。循环在两个需要创建指令的视频轨道间前后切换。
  2. 创建一个新的AVMutableVideoCompositionInstruction实例并设置当前通过CMTimeRange人微言轻它的timeRange属性
  3. 之后为活动组合创建一个新的AVMutableVideoCompositionLayerInstruction,将它添加到数组中并设置它人微言轻组合指令的layerInstructions属性。组合的通过时间范围区域只需要一个与要呈现视频帧的轨道相关的单独指令层指令。
  4. 要创建过渡时间范围指令,需要得到前一个轨道和后一个轨道的引用,按这种方式查找轨道可以确保轨道的引用始终顺序正确。
  5. 创建一个新的AVMutableVideoCompositionInstruction实例,设置当前过渡时间范围为它的timeRange属性
  6. 为每个轨道创建一个AVMutableVideoCompositionLayerInstruction实例。在这些层指令上定义一个从场景到另一个场景的过渡效果。
  7. 将两个层指令添加到NSArray中,并设置它们作为当前组合指令的layerInstructionsn属性。对这一数组中的元素进行排序十分重要,因为它定义了组合输出中视频图层的Z轴顺序。

创建和配置AVVideoComposition

AVMutableVideoComposition* videoComposition = [AVMutableComposition videoComposition];
videoComposition.instructions = compositionInstrutions;
videoComposition.renderSize = CGSizeMake(1280.0f, 720.0f);
videoComposition.frameDuration = CMTimeMake(1, 30);

  • instructions:用于设置以上创建的组合指令。这些指令向组合器描述时间范围和执行组合的种类
  • renderSize:用于定义组合应该被渲染的尺寸,这个值应该对应于组合中的视频原始大小
  • farmeDuration:用于设置有效的帧率。
  • renderScale:定义了视频组合应用的缩放,大部分情况下设置为1.0

应用过渡效果

AVFoundation支持3种过渡类型:溶解、推入和擦除。


溶解

实现溶解效果很容易,只需要对AVMutableVideoCompositionLayerInstruction对象设置一个模糊渐变,在过渡时间内将模糊时间从默认的1.0(完全模糊)调整到0.0(完全透明)。还可以设置交叉溶解,对另一个AVMutableVideoCompositionLayerInstruction设置从0.0(完全透明)到1.0(完全模糊)的渐变即可实现。

[fromLayer setOpacityRampFromStartOpacity:1.0 toEndOpacity:0.0 timeRange:timeRange];

推入

推入就是一个平移的过程,代码如下所示:

CGAffineTransform identityTransform = CGAffineTransformIdentity;
CGFloat videoWidth = videoComposition.renderSize.width;
CGAffineTransform fromDestTransform = CGAffineTransformMakeTranslation(-videoWidth, 0.0);
CGAffineTransform toStartTransform = CGAffineTransformMakeTranslation(videoWidth, 0.0);
[fromLayer setTransformRampFromStartTransform:identityTransform toEndTransform:fromDestTransform timeRange:timeRange];
[toLayer setTransformRampFromStartTransform:toStartTransform toEndTransform:identityTransform timeRange:timeRange];

定义一个对于输入视频层的变换。CGAffineTransform可以修改层的转化、旋转和缩放。对层应用一个渐变的变化可以衍生出许多效果。这里我们将fromLayer移到左侧,使其完全移出视图,并将toLayer从右移动到左。


擦除

CGFloat videoWidth = videoComposition.renderSize.width;
CGFloat videoHeight = videoComposition.renderSize.height;
CGRect startRect = CGRectMake(0.0f, 0.0f, videoWidth, videoHeight);
CGRect endRect = CGRectMake(0.0f, videoHeight, videoWidth, 0.0f);
[fromLayer setCropRectangleRampFromStartCropRectangle:startRect toEndCropRectangle:endRect timeRange:timeRange];

要实现这个效果,需要从视频组合的renderSize中获取宽和高。这些值用于创建擦除动画效果的开始和结束CGRect值。初始矩形为最大的宽度和高度,最终矩形在高度上有所削减,在fromLayer上生成一个向上擦除的效果。


演练

效果图如下,可以看到在三个片断之间的溶解切换。

代码如下所示:






动画层内容

使用CoreAnimation为视频应用程序创建叠加效果的方法同使用它在ios或OS X平台创建实时动画的方法几乎一样,最大的区别在于运行动画的时间模型,当创建实时动画时,CAAnimation实例从系统主机时钟获取执行时间。

主机时间从系统启动开始计算并单向向前推进,将动画执行时间同主机时间相关联在实时动画方面非常适用,但是对于创建视频动画来说就不合适了。视频动画需要基于“影片时间”来操作。开始的时间应该是影片开始的时间。主机时间一直向前推进,不会停止、倒退或快进。因为动画时间需要与视频时间轴绑定,所以需要使用不同的执行时间模式。


AVSynchronizedLayer

AVFoundation提供了一个专门与CALayer子类AVSynchronizedLayer,用于与给定AVPlayerItem实例同步时间。这个图层本身并不展示任何内容。仅用来于图层子树协同时间。这样所有在继承关系中附属于该图层的动画都可以从激活的AVPlayerItem实例中获取相应的执行时间。

通常使用AVSynchronizedLayer时会将其整合到播放器视图的图层继承关系中。同步图层直接呈现在视频图层之上。这样就可以添加动画标题、水印或下沿字幕到播放的视频中,并与播放器的播放栏行为保持完美同步。


AVVideoCompositionCoreAnimationTool

要将CoreAnimation图层和动画整合到导出视频中,就需要使用AVVideoCompositionCoreAnimationTool类。在创建叠加效果时应该注意以下几个问题:


  • CoreAnimation框架的默认行为是执行动画并在动画行为完成后进行处理。通常这些行为就是我们希望在实时案例中使用的,因为时间一旦过去就没法返回了。不过对于视频动画就会有问题,所以需要将动画的removedOnCompletion属性设置为NO。如果没有设置,则动画效果是一次性的,那么重新播放视频的时候,就无法再次看到动画。
  • 动画的beginTime属性被设置为0.0的话是不会看到动画效果的。CoreAnimation将值为0.0的beginTime对象转换为CACurrentMediaTime(),这是当前主机时间。如果希望在影片开头加入动画,将动画的beginTime属性设置为AVCoreAnimationBeginTimeZero常量。

很简单,其中layer参数即为添加了对应元素及动画的CALayer,playerItem即为对应的AVPlayerItem。


导出CoreAnimation

在导出场景中整合CoreAnimation图层,需要使用AVVideoCompositionCoreAnimationTool对象,这个对象的使用比较简单,如下所示:

使用该工具需要注意以下几点:


  • 首先创建两个CALayer实例,组合的视频帧被放在videoLayer中,animationLayer将被渲染并生成导出使用的最终视频帧
  • videoLayer必须是animationLayer的subLayer,同时设置animationLayer的geometryFlipped属性为YES来确保标题被正确渲染。如果没有设置这个值将导致位置颠倒

演练

可以看到,在屏幕的左上角,有一行文本”This is a Test Text Layer”不停的闪烁。

#import
#import "ViewController.h"
#define k720pSize CGSizeMake(1280.0f, 720.0f)
@interface ViewController ()
@property(nonatomic, strong) AVPlayerLayer* thePlayerLayer;
@end
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
AVAsset* asset = [AVAsset assetWithURL:[[NSBundle mainBundle] URLForResource:@"seg3" withExtension:@"mp4"]];
[asset loadValuesAsynchronouslyForKeys:@[@"tracks"] completionHandler:^
dispatch_async(dispatch_get_main_queue(), ^
[self playAsset:asset];
);
return ;
];
return ;
-(void)playAsset:(AVAsset*)asset
AVPlayerItem* playerItem = [AVPlayerItem playerItemWithAsset:asset];
AVPlayer* player = [AVPlayer playerWithPlayerItem:playerItem];
_thePlayerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
_thePlayerLayer.frame = self.view.bounds;
[self.view.layer addSublayer:_thePlayerLayer];
AVSynchronizedLayer* syncLayer = [AVSynchronizedLayer synchronizedLayerWithPlayerItem:playerItem];
CALayer* textLayer = [self buildTextLayerWithText:@"This is a Test Text Layer"];
[syncLayer addSublayer:textLayer];
[_thePlayerLayer addSublayer:syncLayer];
[player play];
return ;
-(void)viewWillLayoutSubviews
[super viewWillLayoutSubviews];
_thePlayerLayer.frame = self.view.bounds;
return ;
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
return UIInterfaceOrientationPortrait;
-(BOOL)shouldAutorotate
return YES;
-(UIInterfaceOrientationMask)supportedInterfaceOrientations
return UIInterfaceOrientationMaskAllButUpsideDown;
-(CALayer*)buildTextLayerWithText:(NSString*)text
CATextLayer* textLayer = [[CATextLayer alloc] init];
textLayer.string = text;
textLayer.fontSize = 20.0f;
textLayer.foregroundColor = [UIColor redColor].CGColor;
textLayer.frame = CGRectMake(0, 0, k720pSize.width, k720pSize.height);
CABasicAnimation* anim = [CABasicAnimation animation];
anim.keyPath = @"foregroundColor";
anim.byValue = (id)[UIColor redColor].CGColor;
anim.toValue = (id)[UIColor yellowColor].CGColor;
anim.removedOnCompletion = NO;
anim.repeatCount = HUGE_VALF;
anim.duration = 2.0f;
anim.beginTime = AVCoreAnimationBeginTimeAtZero;
anim.fillMode = kCAFillModeForwards;
[textLayer addAnimation:anim forKey:nil];
return textLayer;
@end

至此,关于《AVFoundation开发秘籍》的全部内容结束。


推荐阅读
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 2022年的风口:你看不起的行业,真的很挣钱!
    本文介绍了2022年的风口,探讨了一份稳定的副业收入对于普通人增加收入的重要性,以及如何抓住风口来实现赚钱的目标。文章指出,拼命工作并不一定能让人有钱,而是需要顺应时代的方向。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • 小程序自动授权和手动接入的方式及操作步骤
    本文介绍了小程序支持的两种接入方式:自动授权和手动接入,并详细说明了它们的操作步骤。同时还介绍了如何在两种方式之间切换,以及手动接入后如何下载代码包和提交审核。 ... [详细]
author-avatar
Rony通_184_176
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有